package org.knight.gearbox.security;

import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

@Component
public class CustomMethodSecurityMetadataSource implements MethodSecurityMetadataSource {

    private Map<String, List<ConfigAttribute>> resoucesMap = new HashMap<>();
    private final Logger logger = LoggerFactory.getLogger(getClass());

    public CustomMethodSecurityMetadataSource() {

        Properties properties = new Properties();

        try (InputStream in = ClassLoader.getSystemResourceAsStream("method-accesscontrol.properties")) {
            properties.load(in);
        } catch (IOException e) {
            logger.warn("", e);
        }


        for (String key : properties.stringPropertyNames()) {
            String values = properties.getProperty(key);
            Arrays.asList(values.split(","))
                    .stream()
                    .forEach(method -> {
                        List<ConfigAttribute> roles = resoucesMap.get(method);
                        if (roles == null) {
                            roles = new ArrayList<>();
                            resoucesMap.put(method, roles);
                        }
                        roles.add(new SecurityConfig(key));
                    });
        }
    }


    @Override
    public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
        String key = targetClass.getCanonicalName() + "#" + method.getName();
        return resoucesMap.get(key);
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        MethodInvocation mi = (MethodInvocation)object;
        Object target = mi.getThis();

        Class<?> targetClazz = null;
        if (target != null) {
            targetClazz = target instanceof Class<?> ?
                    (Class<?>) target : AopProxyUtils.ultimateTargetClass(target);
        }

        Collection<ConfigAttribute> attrs = getAttributes(mi.getMethod(),targetClazz);

        if (attrs != null && !attrs.isEmpty()) {
            return  attrs;
        }

        if (target != null && !(target instanceof  Class<?>)) {
            attrs = getAttributes(mi.getMethod(), target.getClass());
        }
        return attrs;

    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> tmp = new HashSet<>();

        resoucesMap.values()
                .stream()
                .forEach( configAttrs -> {
                    tmp.addAll(configAttrs);
                });
        return tmp;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return MethodInvocation.class.isAssignableFrom(clazz);
    }
}
